---
title: bud.hooks
description: Modify internal bud values with callbacks.
---

**bud.hooks** allows for modifying values with callbacks. This is useful for things like logging, or for modifying a value before it is used.

## bud.hooks.on

Callbacks and raw values are registered with `bud.hooks.on`

`bud.hooks.on` takes two parameters:

1. The `name` of the hook.
2. Either a raw value or a callback which is passed the current value

### Example defined with a raw value

```ts title=bud.config.ts
bud.hooks.on('build.externals', {
  $: 'jquery',
})
```

### Example defined with a callback

You should always assume the value passed to the callback may be **undefined** and handle it appropriately.

```ts title=bud.config.ts
bud.hooks.on('build.externals', (externals = {}) => ({
  ...externals,
  $: 'jquery',
})
```

## bud.hooks.filter

Filters are registered with `bud.hooks.filter`.

```ts title=bud.config.ts
const initialValue = `foo`
const result = bud.hooks.filter('my.filter.key', initialValue)
```

`bud.hooks.filter` takes two parameters:

- The `name` of the hook.
- An initial value which is passed to the first registered function. If there is nothing hooked to this filter, the value will be returned as-is.

## bud.hooks.async

Some hooks are asynchronous. An example of this are values for `build.resolve.modules`:

```ts title=bud.config.ts
bud.hooks.async('build.resolve.modules', async (paths = []) => [
  ...paths,
  bud.path(`modules`),
])
```

Callbacks for asynchronous hooks should be registered as asynchronous functions.

## bud.hooks.action

Actions are registered with `bud.hooks.action`. Every action is passed the **bud** object and it will never be **undefined**.
You don't need to return anything.

```ts title=bud.config.ts
bud.hooks.action('my.action.key', async bud => {
  bud.log('action log') // do something
})
```

Multiple actions can be defined for the same key in one call:

```ts title=bud.config.ts
bud.hooks.action(
  `my.action.key`,
  async bud => bud.log(`action log 1`),
  async bud => bud.log(`action log 2`),
  async bud => bud.log(`action log 3`),
)
```

All actions are asynchronous. The order of execution is not guaranteed. This matters whether you are
specifying a single action or multiple actions.

## bud.hooks.fire

Actions are fired with `bud.hooks.fire`.

```ts title=bud.config.ts
await bud.hooks.fire('my.action.key')
```

## Putting it together

```ts title=bud.config.ts
bud.hooks.filter(`my.hook`, 1)
// => 1

bud.hooks.on(`my.hook`, (value = 0) => value + 1)

bud.hooks.filter(`my.hook`, 1)
// => 2

bud.hooks.on(`my.hook`, 5)

bud.hooks.filter(`my.hook`, 1)
// => 5 (since the last registered value is not a callback)

bud.hooks.action(`my.action`, async bud => bud.log(`action log`))

await bud.hooks.fire(`my.action`)
// [stdout] action log
```

## What to do when a hook value is a function

Consider this example, where we want to get a function that has been registered to a hook. We only want to run the
last registered function and we only want to call it one time:

```ts title=bud.config.ts
const doThing = () => {
  console.log(`thing`)
}
bud.hooks.on(`my.hook`, doThing)

const doThing2 = () => {
  console.log(`thing2`)
}
bud.hooks.on(`my.hook`, doThing2)

const myFunction = bud.hooks.filter(`my.hook`)
myFunction()
```

This is not what we want. That will log both `thing` and `thing2` and then cause a `TypeError` (because `myFunction` is `undefined`).

But it makes sense -- every registered hook will always be called when
the filter runs, and the functions we defined don't return anything. So the end result is `undefined` and every
`console.log` call happened as each hook function was called.

The way around it is to wrap our functions in a callback:

```ts title=bud.config.ts
const doThing = () => {
  console.log(`thing`)
}
bud.hooks.on(`my.hook`, () => doThing)

const doThing2 = () => {
  console.log(`thing2`)
}
bud.hooks.on(`my.hook`, () => doThing2)

const myFunction = bud.hooks.filter(`my.hook`)
myFunction()
```

Now it will only log `thing2` from `doThing2`. `doThing` will remain unexecuted.

Something to remember if you ever need to pass a function around in a hook.
